<?php
// *------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// *------------
// | Copyright (c) 2006-2012 http://thinkphp.cn All rights reserved.
// *------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// *------------
// | Author: liu21st <liu21st@gmail.com>
// *------------
// $Id: DbMysql.class.php 2791 2012-02-29 10:08:57Z liu21st $
define('CLIENT_MULTI_RESULTS', 131072);
/**
 *--------------------
 * Mysql数据库驱动类
 *--------------------
 * @category   Think
 * @package  Think
 * @subpackage  Db
 * @author    liu21st <liu21st@gmail.com>
 * @version   $Id: DbMysql.class.php 2791 2012-02-29 10:08:57Z liu21st
 * @last modify 2012-12-10 18:00:45 by mashanling $
 *--------------------
 */
class DbMysql extends Db {

    /**
     * 获得所有的查询数据
     *
     * @last modify 2012-12-10 18:01:44 by mashanling
     *
     * @access private
     *
     * @return array 数据集
     */
    private function getAll($key_column = false) {
        $result = array();//返回数据集

        if ($this->num_rows > 0) {

            if ($key_column) {//返回数组键值，用空间换时间 by mashanling on 2012-12-20 17:59:46
                while ($row = mysql_fetch_assoc($this->query_id)) {
                    $result[$row[$key_column]] = $row;
                }
            }
            else {
                while ($row = mysql_fetch_assoc($this->query_id)) {
                    $result[] = $row;
                }
            }

            mysql_data_seek($this->query_id, 0);
        }

        return $result;
    }

    /**
     * 构造函数 读取数据库配置信息
     *
     * @last modify 2012-12-10 18:02:13 by mashanling
     *
     * @access public
     *
     * @param array $config 数据库配置数组
     *
     * @return void 无返回值
     */
    public function __construct($config = '') {

        if (!extension_loaded('mysql')) {
            throw new Exception(L('_NOT_SUPPERT_') . ':mysql');
        }

        if (!empty($config)) {
            $this->config = $config;

            if (empty($this->config['params'])) {
                $this->config['params'] = array();
            }
        }
    }

    /**
     * 关闭数据库
     *
     * @last modify 2012-12-10 18:03:24 by mashanling
     *
     * @access public
     *
     * @return void 无返回值
     */
    public function close() {
        $this->_link_id && mysql_close($this->_link_id);
        $this->_link_id = null;
    }

    /**
     * 用于非自动提交状态下面的查询提交
     *
     * @last modify 2012-12-10 18:04:18 by mashanling
     *
     * @access public
     * @throws Execption
     *
     * @return bool true
     */
    public function commit() {

        if ($this->trans_times > 0) {
            $result = mysql_query('COMMIT', $this->_link_id);
            $this->trans_times = 0;

            if (!$result) {
                throw new Exception($this->error());
            }
        }

        return true;
    }

    /**
     * 连接数据库方法
     *
     * @last modify 2012-12-10 18:04:48 by mashanling
     *
     * @access public
     *
     * @param array $config   配置信息
     * @param int   $link_num 连接号
     * @param bool  $fource
     *
     * @return resource 数据库连接资源
     */
    public function connect($config = '', $link_num = 0, $force = false) {

        if (!isset($this->link_id[$link_num])) {

            if (empty($config)) {
                $config = $this->config;
            }

            //处理不带端口号的socket连接情况
            $host = $config['hostname'] . ($config['hostport'] ? ':' . $config['hostport'] : '');

            //是否长连接
            $pconnect = empty($config['params']['persist']) ? $this->pconnect : $config['params']['persist'];

            if ($pconnect) {
                $this->link_id[$link_num] = mysql_pconnect($host, $config['username'], $config['password'], CLIENT_MULTI_RESULTS);
            }
            else {
                $this->link_id[$link_num] = mysql_connect($host, $config['username'], $config['password'], true, CLIENT_MULTI_RESULTS);
            }

            if (!$this->link_id[$link_num] || (!empty($config['database']) && !mysql_select_db($config['database'], $this->link_id[$link_num]))) {
                throw new Exception(mysql_error());
            }

            $db_version = mysql_get_server_info($this->link_id[$link_num]);

            //使用UTF8存取数据库 需要mysql 4.1.0以上支持
            $db_version >= '4.1' && mysql_query('SET NAMES ' . C('DB_CHARSET'), $this->link_id[$link_num]);
            $db_version > '5.0.1' && mysql_query("SET sql_mode=''", $this->link_id[$link_num]);//设置 sql_model

            $this->connected = true;//标记连接成功

            //注销数据库连接配置信息
            if (1 != C('DB_DEPLOY_TYPE')) {
                unset($this->config);
            }
        }

        return $this->link_id[$link_num];
    }//end connect

    /**
     * 数据库错误信息 并显示当前的SQL语句
     *
     * @last modify 2012-12-10 18:11:05 by mashanling
     *
     * @access public
     *
     * @return string 数据库错误信息
     */
    public function error() {
        $this->error = mysql_error($this->_link_id);

        //写sql错误日志 by mashanling on 2012-09-05 11:38:25
        sys_config('sys_log_sqlerror') && false === strpos($this->query_str, TB_LOG) && D(MODULE_NAME)->addLog($this->query_str . '<br />' . $this->error);

        return $this->error;
    }

    /**
     * SQL指令安全过滤
     *
     * @last modify 2012-12-10 18:16:34 by mashanling
     *
     * @access public
     *
     * @param string $str SQL字符串
     *
     * @return string 安全过滤后的SQL字符串
     */
    public function escapeString($str) {

        if ($this->_link_id) {
            return mysql_real_escape_string($str, $this->_link_id);
        }
        else {
            return addslashes($str);
        }
    }

    /**
     * 执行语句
     *
     * @last modify 2012-12-10 18:17:03 by mashanling
     *
     * @access public
     *
     * @param string $str  sql指令
     *
     * @return mixed 执行成功，返回影响行数，否则返回false
     *
     */
    public function execute($str) {
        $this->initConnect(true);

        if (!$this->_link_id) {
            return false;
        }

        $this->query_str = $str;
        $this->query_id && $this->free();//释放前次的查询结果

        N('db_write', 1);

        G('queryStartTime');//记录开始执行时间
        $result = mysql_query($str, $this->_link_id);
        G('queryEndTime');

        if (false === $result) {
            $this->error();
        }
        else {
            $this->num_rows = mysql_affected_rows($this->_link_id);
            $this->last_insert_id = mysql_insert_id($this->_link_id);
            $result = $this->num_rows;
        }

        $this->debug();

        return $result;
    }//end execute

    /**
     * 释放查询结果
     *
     * @last modify 2012-12-10 18:20:04 by mashanling
     *
     * @access public
     *
     * @return void 无返回值
     */
    public function free() {
        mysql_free_result($this->query_id);
        $this->query_id = null;
    }

    /**
     * 取得数据表的字段信息
     *
     * @last modify 2012-12-10 18:20:33 by mashanling
     *
     * @access public
     *
     * @param string $table_name 表名
     *
     * @return array 表字段信息
     */
    public function getFields($table_name) {
        $result = $this->query('SHOW COLUMNS FROM ' . $this->parseKey($table_name));
        $info   = array();

        if ($result) {

            foreach ($result as $key => $val) {
                $info[$val['Field']] = array(
                    'name' => $val['Field'],
                    'type' => $val['Type'],
                    'notnull' => '' === $val['Null'],  // not null is empty, null is yes
                    'default' => $val['Default'],
                    'primary' => 'pri' == strtolower($val['Key']),
                    'autoinc' => 'auto_increment' == strtolower($val['Extra'])
                );
            }
        }

        return $info;
    }

    /**
     * 取得数据库的表信息
     *
     * @last modify 2012-12-10 18:21:13 by mashanling
     *
     * @access public
     *
     * @param string $db_name 数据库名
     *
     * @return array 数据库表信息
     */
    public function getTables($db_name = '') {
        $sql    = 'SHOW TABLES' . ($db_name ? 'FROM ' . $db_name : '');
        $result = $this->query($sql);
        $info   = array();

        if ($result) {

            foreach ($result as $key => $val) {
                $info[$key] = current($val);
            }
        }

        return $info;
    }

    /**
     * 插入所有记录
     *
     * @last modify 2012-12-10 18:22:43 by mashanling
     *
     * @access public
     *
     * @param array   $datas    数据
     * @param array   $options  参数表达式
     * @param boolean $replace  是否replace
     *
     * @return mixed $this->execute()返回结果
     */
    public function insertAll($datas, $options = array(), $replace = false) {

        if (!is_array($datas[0])) {
            return false;
        }

        $fields = array_keys($datas[0]);
        array_walk($fields, array($this, 'parseKey'));
        $values = array();

        foreach ($datas as $data) {
            $value = array();

            foreach ($data as $key => $val) {
                $val = $this->parseValue($val);

                if (is_scalar($val)) {// 过滤非标量数据
                    $value[] = $val;
                }
            }
            $values[] = '(' . implode(',', $value) . ')';
        }
        $sql = ($replace ? 'REPLACE' : 'INSERT') . ' INTO ' . $this->parseTable($options['table']) . ' (' . implode(',', $fields) . ') VALUES ' . implode(',', $values);

        return $this->execute($sql);
    }//end insertAll

    /**
     * 字段和表名处理添加`
     *
     * @last modify 2012-12-11 16:27:50 by mashanling
     *
     * @access protected
     *
     * @param string $key 字段
     *
     * @return string 处理后字段
     */
    protected function parseKey(&$key) {
        $key = trim($key);

        if( '*' == $key || false !== strpos($key, '(') || false !== strpos($key, '.') || false !== strpos($key, '`')) {
        }
        else {
            $key = '`' . $key . '`';
        }

        return $key;
    }

    /**
     * 执行查询 返回数据集
     *
     * @last modify 2012-12-11 16:27:46 by mashanling
     *
     * @access public
     *
     * @param string $str sql指令
     *
     * @return mixed 查询出错，返回false，否则返回$this->getAll()返回结果
     */
    public function query($str, $key_column = false) {

        if (0 === stripos($str, 'call')) { // 存储过程查询支持
            $this->close();
        }

        $this->initConnect(false);

        if (!$this->_link_id) {
            return false;
        }

        $this->query_str = $str;
        $this->query_id && $this->free();//释放前次的查询结果

        N('db_query', 1);

        G('queryStartTime');//记录开始执行时间
        $this->query_id = mysql_query($str, $this->_link_id);
        G('queryEndTime');

        if (false === $this->query_id) {
            $this->error();
            $result = false;
        }
        else {
            $this->num_rows = mysql_num_rows($this->query_id);
            $result = $this->getAll($key_column);
        }

        $this->debug();

        return $result;
    }//end query

    /**
     * 替换记录
     *
     * @last modify 2012-12-11 16:28:50 by mashanling
     *
     * @access public
     *
     * @param mixed $data    数据
     * @param array $options 参数表达式
     *
     * @return mixed $this->execute()返回结果
     */
    public function replace($data, $options = array()) {

        foreach ($data as $key => $val) {
            $value = $this->parseValue($val);

            if (is_scalar($value)) {// 过滤非标量数据
                $values[] = $value;
                $fields[] = $this->parseKey($key);
            }
        }
        $sql = 'REPLACE INTO ' . $this->parseTable($options['table']) . ' (' . implode(',', $fields) . ') VALUES (' . implode(',', $values) . ')';

        return $this->execute($sql);
    }

    /**
     * 事务回滚
     *
     * @last modify 2012-12-11 16:28:50 by mashanling
     *
     * @access public
     * @throws Execption
     *
     * @return bool true
     */
    public function rollback() {

        if ($this->trans_times > 0) {
            $result = mysql_query('ROLLBACK', $this->_link_id);
            $this->trans_times = 0;

            if (!$result) {
                throw new Exception($this->error());
            }
        }

        return true;
    }

    /**
     * 启动事务
     *
     * @access public
     *
     * @last modify 2012-12-11 16:28:50 by mashanling
     *
     * @return void 无返回值
     */
    public function startTrans() {
        $this->initConnect(true);

        if (!$this->_link_id) {
            return false;
        }

        if (0 === $this->trans_times) {//数据rollback支持
            mysql_query('START TRANSACTION', $this->_link_id);
        }

        $this->trans_times++;
    }
}